﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using InfoReaderLib.Info;
using Newtonsoft.Json;
using Windows.Foundation;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Net.Http;
using System.Text.RegularExpressions;

namespace InfoReaderLib.Parser.SimpleParser
{
    /// <summary>
    /// Process a single level website, first level is list, which contains link to detail pages
    /// </summary>
    public sealed class Parser : IParser
    {
        private string _SettingJSON;

        public string SettingJSON
        {
            get { return _SettingJSON; }
            set
            {
                _SettingJSON = value;
                try
                {
                    this._Setting = JsonConvert.DeserializeObject<Setting>(_SettingJSON);
                    Util.Debug(this._Setting.ListRegEx);
                }
                catch (Exception ex)
                {
                    Util.Debug(ex.StackTrace);
                }
            }
        }

        public IInfoSource DataSource { get; set; }

        private Setting _Setting;

        public IAsyncOperation<IInfo> Parse()
        {
            return AsyncInfo.Run(async _ => (IInfo) await ParseList());
        }

        public IAsyncOperation<IInfo> Parse(IInfoDetail detailSource)
        {
            return AsyncInfo.Run(async _ => (IInfo) await ParseDetail((InfoDetail)detailSource));
        }

        /// <summary>
        /// Just use DataSource to obtain list
        /// </summary>
        /// <returns></returns>
        private async Task<InfoList> ParseList()
        {
            if (this._Setting == null || this.DataSource == null || this.DataSource.Url == null) return null;

            var returnList = new InfoList()
            {
                List = new List<IInfo>()
            };

            // query server
            HttpClient client = new HttpClient();
            Task<string> htmlTask = client.GetStringAsync(this.DataSource.Url);

            string html = await htmlTask;
            //Util.Debug(html);

            // parse
            // use singeline mode
            var regexObj = new Regex(this._Setting.ListRegEx, RegexOptions.Singleline);
            var matchObj = regexObj.Match(html);
            while (matchObj.Success)
            {
                Util.Debug("Match=" + matchObj.Index);
                // extract info from groups according to setting
                string author = ExtractTextFromRegExGroup(matchObj.Groups, this._Setting.ListRegExMapping.Author);
                string date = ExtractTextFromRegExGroup(matchObj.Groups, this._Setting.ListRegExMapping.Date);
                string title = ExtractTextFromRegExGroup(matchObj.Groups, this._Setting.ListRegExMapping.Title);
                string url = ExtractTextFromRegExGroup(matchObj.Groups, this._Setting.ListRegExMapping.Url);
                Util.Debug("author=" + author);
                Util.Debug("date=" + date);
                Util.Debug("title=" + title);
                Util.Debug("url=" + url);

                // construct InfoDetail, store in return list
                returnList.List.Add(new InfoDetail()
                {
                    Author = author,
                    CreateDateText = date,
                    Title = title,
                    Url = url
                });

                // advance
                matchObj = matchObj.NextMatch();
            }

            return returnList;
        }

        /// <summary>
        /// Obtain detail
        /// </summary>
        /// <returns></returns>
        private async Task<InfoDetail> ParseDetail(InfoDetail detailParam)
        {
            if (this._Setting == null || this.DataSource == null || this.DataSource.Url == null || detailParam == null || detailParam.Url == null) return null;

            Util.Debug("detailParam=" + detailParam.Url);

            // query server
            HttpClient client = new HttpClient();
            Task<string> htmlTask = client.GetStringAsync(detailParam.Url);

            string html = await htmlTask;
            //Util.Debug(html);

            // parse
            // use singeline mode
            var regexObj = new Regex(this._Setting.DetailRegEx, RegexOptions.Singleline);
            var matchObj = regexObj.Match(html);
            if (matchObj.Success)
            {
                Util.Debug("Match=" + matchObj.Index);
                // extract info from groups according to setting
                string author = ExtractTextFromRegExGroup(matchObj.Groups, this._Setting.DetailRegExMapping.Author);
                string date = ExtractTextFromRegExGroup(matchObj.Groups, this._Setting.DetailRegExMapping.Date);
                string title = ExtractTextFromRegExGroup(matchObj.Groups, this._Setting.DetailRegExMapping.Title);
                string article = ExtractTextFromRegExGroup(matchObj.Groups, this._Setting.DetailRegExMapping.Article);
                Util.Debug("author=" + author);
                Util.Debug("date=" + date);
                Util.Debug("title=" + title);
                Util.Debug("article=" + article);

                // construct InfoDetail, store in return list
                detailParam.Author = author;
                detailParam.CreateDateText = date;
                detailParam.Title = title;
                detailParam.Article = article;
                return detailParam;
            }

            return null;
        }

        /// <summary>
        /// Extract text from RegEx match groups according to given index.
        /// Concat with space character in between if more than one index.
        /// </summary>
        /// <param name="matchGroups"></param>
        /// <param name="groupIndexList"></param>
        /// <returns></returns>
        private string ExtractTextFromRegExGroup(GroupCollection matchGroups, IList<int> groupIndexList)
        {
            string returnText = "";
            int groupCount = matchGroups.Count;
            if (groupCount == 0)
                return returnText;
            foreach (int groupIndex in groupIndexList)
            {
                if (groupCount <= groupIndex)
                    break;
                if (returnText != "")
                    returnText += " ";
                returnText += matchGroups[groupIndex];
            }
            return returnText;
        }
    }
}
